<!DOCTYPE html>
<!-- saved from url=(0072)#t01.%E5%AE%89%E8%A3%85 -->
<html lang="en" class="trancy-zh-CN" style="height: 3762px;"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>珠峰架构师成长计划</title>
    <link rel="stylesheet" type="text/css" href="./珠峰架构师成长计划_files/main.css">
    <link rel="stylesheet" href="https://static.zhufengpeixun.com/bootstrapmin_1645176572503.css">
    <style>
        .page-toc > ul .red {
            background: #f3f3f3;
            z-index: 1;
            border-left: 3px solid #009a61;
            -webkit-transition: all .2s ease;
            transition: all .2s ease;
            color: #000
        }
    </style>
    <style>
        html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-weight:inherit;font-style:inherit;font-family:inherit;font-size:100%;vertical-align:baseline}body{line-height:1;color:#000;background:#fff}ol,ul{list-style:none}table{border-collapse:separate;border-spacing:0;vertical-align:middle}caption,th,td{text-align:left;font-weight:normal;vertical-align:middle}a{text-decoration:inherit}a:hover{text-decoration:underline}a img{border:none}*{box-sizing:border-box}article,aside,canvas,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary,main{margin:0;padding:0;border:0;outline:0;display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]),[hidden]{display:none}.markdown-body{padding:30px 35px 30px 35px;word-wrap:break-word;font-family:"Helvetica Neue",Helvetica,"Segoe UI",Arial,freesans,sans-serif;font-size:16px;line-height:1.4em;color:#333}@media screen and (max-width:479px){.markdown-body{padding:30px 10px 30px 10px}}.markdown-body>*:first-child{margin-top:0 !important}.markdown-body strong{font-weight:bold}.markdown-body hr{border-top:1px solid #cacaca;border-width:1px 0 0 0}.markdown-body em{font-style:italic}.markdown-body img{max-width:100%}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:bold;line-height:1.4}.markdown-body h1 a,.markdown-body h2 a,.markdown-body h3 a,.markdown-body h4 a,.markdown-body h5 a,.markdown-body h6 a{display:none}.markdown-body h1:hover a,.markdown-body h2:hover a,.markdown-body h3:hover a,.markdown-body h4:hover a,.markdown-body h5:hover a,.markdown-body h6:hover a{display:inline;color:#000;font-size:85%}.markdown-body h1,.markdown-body h2{border-bottom:1px solid #eee}.markdown-body h1{font-size:2.25em;line-height:1.2;padding-bottom:.3em}.markdown-body h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px dashed #dedede}.markdown-body blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd;margin:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body p,.markdown-body blockquote,.markdown-body ul,.markdown-body ol,.markdown-body dl,.markdown-body table,.markdown-body pre{margin-top:0;margin-bottom:16px}.markdown-body ul,.markdown-body ol{padding-left:1.4em;list-style:initial}.markdown-body ol{list-style-type:decimal}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ul ul ol,.markdown-body ul ol ol,.markdown-body ol ul ol,.markdown-body ol ol ol{list-style-type:lower-alpha}.markdown-body pre{padding:16px;overflow:auto;background-color:#e5e5e5;border-radius:3px;word-break:break-all;word-wrap:break-word;font:16px Consolas,"Liberation Mono",Menlo,Courier,monospace}.markdown-body pre code{background-color:transparent;color:inherit;line-height:16px;display:block;font-size:14px}.markdown-body pre code:before,.markdown-body pre code:after,.markdown-body pre tt:before,.markdown-body pre tt:after{letter-spacing:0;content:""}.markdown-body code,.markdown-body tt{padding:0;padding-bottom:2px;margin:0 3px;vertical-align:top;background-color:#ededf7;border-radius:4px;padding-left:3px;padding-right:3px;font-size:14px;color:#6f5990}.markdown-body code:before,.markdown-body code:after,.markdown-body tt:before,.markdown-body tt:after{content:"\00a0";vertical-align:text-top}.markdown-body table{width:100%;border-collapse:collapse;border-spacing:0;max-width:100%;display:block;background-color:transparent}.markdown-body table th,.markdown-body table td{border:1px solid #ddd;padding:4px 10px}.markdown-body table th{font-weight:bold;background:#f3f3f3}.markdown-body table tr:nth-child(2n){background-color:#f8f8f8}.markdown-body table tbody{background:#fff}.markdown-body .task-list-item{padding:0}.markdown-body .task-list-item li{list-style-type:none}.hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8;-webkit-text-size-adjust:none}.hljs-comment,.diff .hljs-header{color:#969896;font-size:90%}.hljs-keyword,.css .rule .hljs-keyword,.hljs-winutils,.nginx .hljs-title,.hljs-subst,.hljs-request,.hljs-status{color:#a71d5d;font-weight:bold}.hljs-number,.hljs-hexcolor,.ruby .hljs-constant{color:#008080}.hljs-string,.hljs-tag .hljs-value,.hljs-doctag,.tex .hljs-formula{color:#183691}.hljs-title,.hljs-id,.scss .hljs-preprocessor{color:#900;font-weight:bold}.hljs-list .hljs-keyword,.hljs-subst{font-weight:normal}.hljs-class .hljs-title,.hljs-type,.vhdl .hljs-literal,.tex .hljs-command{color:#458;font-weight:bold}.hljs-tag,.hljs-tag .hljs-title,.hljs-rule .hljs-property,.django .hljs-tag .hljs-keyword{color:#000080;font-weight:normal}.hljs-attribute,.hljs-variable,.lisp .hljs-body,.hljs-name{color:#008080}.hljs-regexp{color:#009926}.hljs-symbol,.ruby .hljs-symbol .hljs-string,.lisp .hljs-keyword,.clojure .hljs-keyword,.scheme .hljs-keyword,.tex .hljs-special,.hljs-prompt{color:#990073}.hljs-built_in{color:#0086b3}.hljs-preprocessor,.hljs-pragma,.hljs-pi,.hljs-doctype,.hljs-shebang,.hljs-cdata{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.diff .hljs-change{background:#0086b3}.hljs-chunk{color:#aaa}*{box-sizing:border-box}body,html{height:100%;font-family:Helvetica,arial,nimbussansl,liberationsans,freesans,clean,sans-serif,"Segoe UI Emoji","Segoe UI Symbol"}.logo{float:left;padding:10px 20px 0 0;font-size:24px;color:#61dafb}.logo img{vertical-align:middle;margin:-4px 0 0 0}.nav{background:#20232a;z-index:2;height:80px;line-height:61px;border-bottom:1px solid #c4c4c4;padding:0 0 0 20px;position:fixed;width:100%}@media screen and (max-width:479px){.nav{height:initial;position:initial}}.nav ul{list-style:none;position:relative;display:inline-table;display:block;overflow-x:auto;height:100%}@media screen and (max-width:479px){.nav ul{display:-ms-inline-flexbox;display:inline-flex;width:100%;overflow:auto}}.nav ul ul{display:none;background:#d1d1d1;border-radius:0;padding:0;position:absolute;top:100%;z-index:9}.nav ul ul li.active a{color:#61dafb;line-height:29px;height:29px}.nav ul ul li{float:none;border-bottom:1px solid #c8c8c8;position:relative}.nav ul ul li a:hover{background:#4b545f;color:#fff}.nav ul ul a{color:#fff;line-height:29px;white-space:nowrap;word-break:keep-all}.nav ul ul ul{position:absolute;left:100%;top:0}.nav ul li{float:left;line-height:40px}.nav ul li a{color:#fff;text-decoration:none;padding:0 15px}.nav ul li:hover a{color:#61dafb}.nav ul li.active a{color:#61dafb;height:40px}.nav ul li:hover > ul{display:block}.nav ul:after{content:"";clear:both;display:block}.warpper{width:100%;padding:80px 0 0 0;height:100%;overflow:auto}@media screen and (max-width:479px){.warpper{padding-top:0}}.warpper .page-toc,.warpper .markdown-body{height:100%}.warpper .markdown-body{margin-left:220px;overflow:auto}@media screen and (max-width:479px){.warpper .markdown-body{margin-left:0}}.warpper .page-toc{width:200px;height:calc(100% - 80px);position:fixed;width:220px;border-right:1px solid #bbb;box-shadow:0 0 20px #ccc;background:#f7f7f7;padding:10px;overflow-y:auto;overflow-x:hidden;font-family:"Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,sans-serif !important}@media screen and (max-width:479px){.warpper .page-toc{display:none}}.warpper .page-toc ul{list-style-type:none;margin:0}.warpper .page-toc ul a{display:block;padding:3px 0;color:#151515;text-decoration:none;font-weight:700;line-height:16px;font-size:14px}.warpper .page-toc ul a:hover{text-decoration:underline}.warpper .page-toc ul li{padding-left:3px;line-height:25px;text-align:left}.warpper .page-toc ul ul{margin:0 0 0 10px;padding:0 0 0 9px}.warpper .page-toc ul ul li a{font-size:90%;font-weight:normal;border-bottom:0;position:relative}.warpper .copyright{border-top:1px dashed #e9e9e9;padding:6px 0 5px 2px;margin:32px 0 0 0;line-height:16px;font-size:12px;color:#dfdfdf}.warpper .copyright a{color:#d6ddf8;text-decoration:underline}.warpper .copyright a:hover{color:#333}.forkgithub{position:absolute;z-index:2;right:0;background:url("../img/forkgithub.png") 14px -15px no-repeat}@media screen and (max-width:479px){.forkgithub{display:none}}.forkgithub a{display:block;width:149px;height:149px}.my-active{font-weight:bold !important;margin-top:-3px}.my-active:before{content:'';display:block;height:16px;border-left:4px solid #61dafb;padding-left:16px;position:absolute;left:-12px}::-webkit-scrollbar-track-piece{background-color:#fff;-webkit-border-radius:0}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-thumb{height:50px;background-color:#999;-webkit-border-radius:4px;outline:2px solid #fff;outline-offset:-2px;border:2px solid #fff}::-webkit-scrollbar-thumb:hover{height:50px;background-color:#9f9f9f;-webkit-border-radius:4px}
    </style>
</head>
<body>
<div class="nav">
    <div class="logo">
        
                珠峰架构师成长计划
                    
    </div>
    <ul><li><a href="http://zhufengpeixun.com/nestjs/index.html">index</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/1.fe.html">1.fe</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/2.nest.html">2.nest</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/3.decorator.html">3.decorator</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/4.controller.html">4.controller</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/5.ioc.html">5.ioc</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/6.Providers.html">6.Providers</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/7.middleware.html">7.middleware</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/8.Exceptionfilters.html">8.Exceptionfilters</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/9.Pipes.html">9.Pipes</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/10.Guards.html">10.Guards</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/11.Interceptors.html">11.Interceptors</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/12.Upload.html">12.Upload</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/13.cms-rbac.html">13.cms-rbac</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/14.cms-content.html">14.cms-content</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/15.cms-front.html">15.cms-front</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/16.cms-refer.html">16.cms-refer</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/mysql2.html">mysql2</a></li><li class="active"><a href="">typeorm</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/logger.html">logger</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/nestjs-i18n.html">nestjs-i18n</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/Schematic.html">Schematic</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/generator.html">generator</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/htmx.html">htmx</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/websocket.html">websocket</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/etcd.html">etcd</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/gRPC.html">gRPC</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/microservices.html">microservices</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/monorepo.html">monorepo</a></li><li><a href="http://zhufengpeixun.com/nestjs/html/graphql.html">graphql</a></li></ul></div>


  <div class="warpper">
    <span id="toc-toggle" style="position: fixed;left:10px;top:55px;z-index: 1000;color:white;background-color: rgb(32,35,42);">&lt;</span>
    <div class="page-toc">
      <ul><li><a href="#t01.%E5%AE%89%E8%A3%85">1.安装</a></li><li><a href="#t12.%20%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE%E5%BA%93">2. 连接数据库</a><ul><li><a href="#t22.1%20src\index.ts">2.1 src\index.ts</a></li><li><a href="#t32.2%20data-source.ts">2.2 data-source.ts</a></li><li><a href="#t42.3%20User.ts">2.3 User.ts</a></li><li><a href="#t52.4%20tsconfig.json">2.4 tsconfig.json</a></li></ul></li><li><a href="#t63.CRUD">3.CRUD</a><ul><li><a href="#t73.1%20src\index.ts">3.1 src\index.ts</a></li></ul></li><li><a href="#t84.%E6%89%A7%E8%A1%8CSQL%E8%AF%AD%E5%8F%A5">4.执行SQL语句</a><ul><li><a href="#t94.1%20src\index.ts">4.1 src\index.ts</a></li></ul></li><li><a href="#t105.createQueryBuilder">5.createQueryBuilder</a><ul><li><a href="#t115.1%20queryBuilder">5.1 queryBuilder</a></li><li><a href="#t125.2%20src\index.ts">5.2 src\index.ts</a></li></ul></li><li><a href="#t136.%20getRepository">6. getRepository</a><ul><li><a href="#t146.1%20src\index.ts">6.1 src\index.ts</a></li></ul></li><li><a href="#t157.%E4%B8%80%E5%AF%B9%E4%B8%80">7.一对一</a><ul><li><a href="#t167.1%20@OneToOne">7.1 @OneToOne</a></li><li><a href="#t177.2%20@JoinColumn">7.2 @JoinColumn</a></li><li><a href="#t187.3%20cascade">7.3 cascade</a></li><li><a href="#t197.4%20onDelete">7.4 onDelete</a><ul><li><a href="#t207.4.1.%20CASCADE">7.4.1. CASCADE</a></li><li><a href="#t217.4.2%20SET%20NULL">7.4.2 SET NULL</a></li><li><a href="#t227.4.3%20%20RESTRICT">7.4.3  RESTRICT</a></li><li><a href="#t237.4.4%20NO%20ACTION">7.4.4 NO ACTION</a></li><li><a href="#t247.4.5.%20SET%20DEFAULT">7.4.5. SET DEFAULT</a></li></ul></li><li><a href="#t257.5%20User.ts">7.5 User.ts</a></li><li><a href="#t267.6%20Profile.ts">7.6 Profile.ts</a></li><li><a href="#t277.7%20data-source.ts">7.7 data-source.ts</a></li><li><a href="#t287.8%20src\index.ts">7.8 src\index.ts</a></li><li><a href="#t297.9%20%E4%B8%80%E5%AF%B9%E4%B8%80queryBuilder">7.9 一对一queryBuilder</a><ul><li><a href="#t307.9.1%20src\index.ts">7.9.1 src\index.ts</a></li></ul></li></ul></li><li><a href="#t318.%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%92%8C%E5%A4%9A%E5%AF%B9%E4%B8%80">8.一对多和多对一</a><ul><li><a href="#t328.1%20@OneToMany">8.1 @OneToMany</a></li><li><a href="#t338.2%20@ManyToOne">8.2 @ManyToOne</a></li><li><a href="#t348.3%20User.ts">8.3 User.ts</a></li><li><a href="#t358.4%20Order.ts">8.4 Order.ts</a></li><li><a href="#t368.5%20%20src\index.ts">8.5  src\index.ts</a></li></ul></li><li><a href="#t379.%E5%A4%9A%E5%AF%B9%E5%A4%9A">9.多对多</a><ul><li><a href="#t389.1%20@ManyToMany">9.1 @ManyToMany</a></li><li><a href="#t399.2%20@JoinTable">9.2 @JoinTable</a></li><li><a href="#t409.3%20User.ts">9.3 User.ts</a></li><li><a href="#t419.4%20Role.ts">9.4 Role.ts</a></li><li><a href="#t429.5%20index.ts">9.5 index.ts</a></li></ul></li><li><a href="#t4310.%E6%A0%91%E5%9E%8B%E7%BB%93%E6%9E%84">10.树型结构</a><ul><li><a href="#t4410.1%20%E6%A0%91%E5%BD%A2%E7%BB%93%E6%9E%84%E7%B1%BB%E5%9E%8B">10.1 树形结构类型</a><ul><li><a href="#t4510.1.1%20adjacency-list">10.1.1 adjacency-list</a></li><li><a href="#t4610.1.2%20nested-set">10.1.2 nested-set</a></li><li><a href="#t4710.1.3%20closure-table">10.1.3 closure-table</a></li><li><a href="#t4810.1.4%20materialized-path">10.1.4 materialized-path</a></li></ul></li><li><a href="#t4910.2%20Category.ts">10.2 Category.ts</a></li><li><a href="#t5010.3%20src\index.ts">10.3 src\index.ts</a></li></ul></li><li><a href="#t5111.%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%81%E7%A7%BB">11.数据库迁移</a><ul><li><a href="#t5211.1%20migration:create">11.1 migration:create</a></li><li><a href="#t5311.2%20migration:generate">11.2 migration:generate</a></li><li><a href="#t5411.3%20migration:run">11.3 migration:run</a></li><li><a href="#t5511.4%20migration:generate">11.4 migration:generate</a></li><li><a href="#t5611.5%20migration:revert">11.5 migration:revert</a></li><li><a href="#t5711.6%20npm%20scripts">11.6 npm scripts</a></li></ul></li></ul>
    </div>
    <div class="content markdown-body">
      <p>TypeORM 是一个适用于 Node.js 的 ORM（对象关系映射）框架，用于与数据库进行交互。它支持多种数据库系统，包括 MySQL、PostgreSQL、SQLite、MongoDB 等。TypeORM 使得开发者可以使用面向对象的方式来定义和操作数据库中的数据。</p>
<ul>
<li><a href="https://www.npmjs.com/package/mysql2#documentation">mysql2</a></li>
<li><a href="https://typeorm.io/">typeorm</a></li>
</ul>
<h2 id="t01.安装">1.安装 <a href="#t01.%E5%AE%89%E8%A3%85"> # </a></h2>
<pre><code class="lang-js">npm install typeorm reflect-metadata mysql2
</code></pre>
<pre><code class="lang-js">npm install typescript ts-node @types/node --save-dev
</code></pre>
<h2 id="t12. 连接数据库">2. 连接数据库 <a href="#t12.%20%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE%E5%BA%93"> # </a></h2>
<h3 id="t22.1 src\index.ts">2.1 src\index.ts <a href="#t22.1%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-comment">// 从 data-source 模块中导入 AppDataSource</span>
<span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-comment">// 从 entity/User 模块中导入 User 实体</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
<span class="hljs-comment">// 初始化数据源</span>
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
<span class="hljs-comment">// 初始化成功后的操作</span>
<span class="hljs-comment">// 捕获初始化失败的错误，并输出错误信息</span>
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(error))
.finally(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> process.exit(<span class="hljs-number">0</span>))
</code></pre>
<h3 id="t32.2 data-source.ts">2.2 data-source.ts <a href="#t32.2%20data-source.ts"> # </a></h3>
<p>src\data-source.ts</p>
<pre><code class="lang-js"><span class="hljs-comment">// 引入 reflect-metadata 库，为 TypeORM 启用反射支持</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"reflect-metadata"</span>
<span class="hljs-comment">// 从 typeorm 模块中导入 DataSource 类</span>
<span class="hljs-keyword">import</span> { DataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-comment">// 从 ./entity/User 模块中导入 User 实体</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
<span class="hljs-comment">// 创建并导出一个新的 DataSource 实例，配置数据库连接信息</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AppDataSource = <span class="hljs-keyword">new</span> DataSource({
    <span class="hljs-attr">type</span>: <span class="hljs-string">"mysql"</span>,        <span class="hljs-comment">// 数据库类型为 MySQL</span>
    <span class="hljs-attr">host</span>: <span class="hljs-string">"localhost"</span>,    <span class="hljs-comment">// 数据库主机名</span>
    <span class="hljs-attr">port</span>: <span class="hljs-number">3306</span>,           <span class="hljs-comment">// 数据库端口号</span>
    <span class="hljs-attr">username</span>: <span class="hljs-string">"root"</span>,     <span class="hljs-comment">// 数据库用户名</span>
    <span class="hljs-attr">password</span>: <span class="hljs-string">"root"</span>,     <span class="hljs-comment">// 数据库用户密码</span>
    <span class="hljs-attr">database</span>: <span class="hljs-string">"orm"</span>,      <span class="hljs-comment">// 数据库名称</span>
    <span class="hljs-attr">synchronize</span>: <span class="hljs-literal">true</span>,    <span class="hljs-comment">// 是否自动同步实体与数据库表结构</span>
    <span class="hljs-attr">logging</span>: <span class="hljs-literal">true</span>,        <span class="hljs-comment">// 是否启用日志记录</span>
    <span class="hljs-attr">entities</span>: [User],     <span class="hljs-comment">// 实体类数组，指定要使用的实体</span>
}
</code></pre>
<h3 id="t42.3 User.ts">2.3 User.ts <a href="#t42.3%20User.ts"> # </a></h3>
<p>src\entity\User.ts</p>
<pre><code class="lang-js"><span class="hljs-comment">// 从 typeorm 模块中导入 Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn 装饰器</span>
<span class="hljs-keyword">import</span> { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-comment">// 使用 @Entity 装饰器将 User 类标记为一个数据库表，并指定表名为 "users"</span>
@Entity({
    <span class="hljs-attr">name</span>: <span class="hljs-string">"users"</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-comment">// 使用 @PrimaryGeneratedColumn 装饰器将 id 属性标记为主键并自动生成，并添加注释 "Primary key"</span>
    @PrimaryGeneratedColumn({ <span class="hljs-attr">comment</span>: <span class="hljs-string">"Primary key"</span> })
    <span class="hljs-attr">id</span>: number
    <span class="hljs-comment">// 使用 @Column 装饰器将 firstName 属性标记为数据库表中的一列，限制长度为 50</span>
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">50</span> })
    <span class="hljs-attr">firstName</span>: string
    <span class="hljs-comment">// 使用 @Column 装饰器将 lastName 属性标记为数据库表中的一列，限制长度为 50</span>
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">50</span> })
    <span class="hljs-attr">lastName</span>: string
    <span class="hljs-comment">// 使用 @Column 装饰器将 age 属性标记为数据库表中的一列，类型为 "int"</span>
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"int"</span> })
    <span class="hljs-attr">age</span>: number
    <span class="hljs-comment">// 使用 @Column 装饰器将 email 属性标记为数据库表中的一列，要求唯一且不允许为空</span>
    @Column({ <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">nullable</span>: <span class="hljs-literal">false</span> })
    <span class="hljs-attr">email</span>: string
    <span class="hljs-comment">// 使用 @Column 装饰器将 isActive 属性标记为数据库表中的一列，类型为 "boolean"，默认值为 true</span>
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"boolean"</span>, <span class="hljs-attr">default</span>: <span class="hljs-literal">true</span> })
    <span class="hljs-attr">isActive</span>: boolean
    <span class="hljs-comment">// 使用 @CreateDateColumn 装饰器将 createdAt 属性标记为创建时间列</span>
    @CreateDateColumn()
    <span class="hljs-attr">createdAt</span>: <span class="hljs-built_in">Date</span>
    <span class="hljs-comment">// 使用 @UpdateDateColumn 装饰器将 updatedAt 属性标记为更新时间列</span>
    @UpdateDateColumn()
    <span class="hljs-attr">updatedAt</span>: <span class="hljs-built_in">Date</span>
}
</code></pre>
<h3 id="t52.4 tsconfig.json">2.4 tsconfig.json <a href="#t52.4%20tsconfig.json"> # </a></h3>
<p>tsconfig.json</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"compilerOptions"</span>: {
      <span class="hljs-attr">"lib"</span>: [
         <span class="hljs-string">"es5"</span>,
         <span class="hljs-string">"es6"</span>
      ],
      <span class="hljs-attr">"target"</span>: <span class="hljs-string">"es5"</span>,
      <span class="hljs-attr">"module"</span>: <span class="hljs-string">"commonjs"</span>,
      <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"node"</span>,
      <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./build"</span>,
      <span class="hljs-attr">"emitDecoratorMetadata"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"experimentalDecorators"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"sourceMap"</span>: <span class="hljs-literal">true</span>
   }
}
</code></pre>
<h2 id="t63.CRUD">3.CRUD <a href="#t63.CRUD"> # </a></h2>
<table>
<thead>
<tr>
<th>方法名</th>
<th>介绍</th>
</tr>
</thead>
<tbody>
<tr>
<td>save</td>
<td>保存一个新的用户或更新一个已有的用户信息。</td>
</tr>
<tr>
<td>delete</td>
<td>删除一个用户。</td>
</tr>
<tr>
<td>update</td>
<td>更新一个已有用户的信息。</td>
</tr>
<tr>
<td>insert</td>
<td>插入多个用户。</td>
</tr>
<tr>
<td>remove</td>
<td>删除一个用户（与 delete 类似）。</td>
</tr>
<tr>
<td>findBy</td>
<td>根据指定条件查找用户。</td>
</tr>
<tr>
<td>findAndCount</td>
<td>查找所有用户并计数。</td>
</tr>
<tr>
<td>findOne</td>
<td>查找一个符合指定条件的用户。</td>
</tr>
<tr>
<td>findOneBy</td>
<td>根据指定条件查找一个用户（与 findOne 类似）。</td>
</tr>
<tr>
<td>findOneOrFail</td>
<td>查找一个符合指定条件的用户，如果未找到则抛出异常。</td>
</tr>
</tbody>
</table>
<h3 id="t73.1 src\index.ts">3.1 src\index.ts <a href="#t73.1%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-diff">import { AppDataSource } from "./data-source"
import { User } from "./entity/User"
AppDataSource.initialize().then(async () =&gt; {
<span class="hljs-addition">+    // 保存一个新用户</span>
<span class="hljs-addition">+    const user = new User()</span>
<span class="hljs-addition">+    user.firstName = "John"</span>
<span class="hljs-addition">+    user.lastName = "Doe"</span>
<span class="hljs-addition">+    user.age = 25</span>
<span class="hljs-addition">+    user.email = "john.doe@example.com"</span>
<span class="hljs-addition">+    user.isActive = true</span>
<span class="hljs-addition">+    await AppDataSource.manager.save(user)</span>
<span class="hljs-addition">+    console.log("用户已保存: ", user)</span>
<span class="hljs-addition">+    // 查找一个用户</span>
<span class="hljs-addition">+    const foundUser = await AppDataSource.manager.findOne(User, { where: { id: user.id } })</span>
<span class="hljs-addition">+    console.log("找到的用户: ", foundUser)</span>
<span class="hljs-addition">+    // 更新用户信息</span>
<span class="hljs-addition">+    if (foundUser) {</span>
<span class="hljs-addition">+        foundUser.age = 26</span>
<span class="hljs-addition">+        await AppDataSource.manager.save(foundUser)</span>
<span class="hljs-addition">+        console.log("用户已更新: ", foundUser)</span>
<span class="hljs-addition">+    }</span>
<span class="hljs-addition">+    // 删除用户</span>
<span class="hljs-addition">+    if (foundUser) {</span>
<span class="hljs-addition">+        await AppDataSource.manager.remove(foundUser)</span>
<span class="hljs-addition">+        console.log("用户已删除: ", foundUser)</span>
<span class="hljs-addition">+    }</span>
<span class="hljs-addition">+    // 插入多个用户</span>
<span class="hljs-addition">+    const users = [</span>
<span class="hljs-addition">+        { firstName: "Alice", lastName: "Smith", age: 30, email: "alice.smith@example.com", +isActive: true },</span>
<span class="hljs-addition">+        { firstName: "Bob", lastName: "Johnson", age: 35, email: "bob.johnson@example.com", +isActive: true }</span>
<span class="hljs-addition">+    ]</span>
<span class="hljs-addition">+    await AppDataSource.manager.insert(User, users)</span>
<span class="hljs-addition">+    console.log("插入的用户: ", users)</span>
<span class="hljs-addition">+    // 查找并计数</span>
<span class="hljs-addition">+    const [allUsers, userCount] = await AppDataSource.manager.findAndCount(User)</span>
<span class="hljs-addition">+    console.log("所有用户: ", allUsers)</span>
<span class="hljs-addition">+    console.log("用户总数: ", userCount)</span>
<span class="hljs-addition">+    // 查找一个用户 (findOne)</span>
<span class="hljs-addition">+    const singleUser = await AppDataSource.manager.findOne(User, { where: { email: "alice.smith@example.com" } })</span>
<span class="hljs-addition">+    console.log("查找的用户 (findOne): ", singleUser)</span>
<span class="hljs-addition">+    // 根据某个条件查找一个用户 (findOneBy)</span>
<span class="hljs-addition">+    const userByEmail = await AppDataSource.manager.findOneBy(User, { email: "bob.johnson@example.com" })</span>
<span class="hljs-addition">+    console.log("查找的用户 (findOneBy): ", userByEmail)</span>
<span class="hljs-addition">+    // 查找一个用户或失败 (findOneOrFail)</span>
<span class="hljs-addition">+    try {</span>
<span class="hljs-addition">+        const userOrFail = await AppDataSource.manager.findOneOrFail(User, { where: { email: "nonexistent@example.com" } })</span>
<span class="hljs-addition">+        console.log("查找的用户 (findOneOrFail): ", userOrFail)</span>
<span class="hljs-addition">+    } catch (error) {</span>
<span class="hljs-addition">+        console.log("未找到用户 (findOneOrFail)")</span>
<span class="hljs-addition">+    }</span>
<span class="hljs-addition">+}).catch(error =&gt; console.log(error))</span>
</code></pre>
<h2 id="t84.执行SQL语句">4.执行SQL语句 <a href="#t84.%E6%89%A7%E8%A1%8CSQL%E8%AF%AD%E5%8F%A5"> # </a></h2>
<h3 id="t94.1 src\index.ts">4.1 src\index.ts <a href="#t94.1%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> AppDataSource.query(
        <span class="hljs-string">`INSERT INTO users (firstName, lastName, age, email, isActive, createdAt, updatedAt)
         VALUES ('John', 'Doe', 25, 'john.doe@example.com', true, NOW(), NOW())`</span>
    )
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户已插入"</span>)
    <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> AppDataSource.query(<span class="hljs-string">`SELECT * FROM users`</span>)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"查询到的用户: "</span>, users)
    <span class="hljs-keyword">await</span> AppDataSource.query(
        <span class="hljs-string">`UPDATE users SET age = 26 WHERE email = 'john.doe@example.com'`</span>
    )
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户已更新"</span>)
    <span class="hljs-keyword">await</span> AppDataSource.query(
        <span class="hljs-string">`DELETE FROM users WHERE email = 'john.doe@example.com'`</span>
    )
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户已删除"</span>)
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(error))
</code></pre>
<h2 id="t105.createQueryBuilder">5.createQueryBuilder <a href="#t105.createQueryBuilder"> # </a></h2>
<h3 id="t115.1 queryBuilder">5.1 queryBuilder <a href="#t115.1%20queryBuilder"> # </a></h3>
<p><code>createQueryBuilder</code> 是 TypeORM 中用于构建和执行复杂 SQL 查询的强大工具。它允许开发者使用一种面向对象的方式来创建 SQL 查询，而不需要直接编写 SQL 语句。<code>createQueryBuilder</code> 方法返回一个 <code>QueryBuilder</code> 实例，该实例提供了一系列方法来构建查询。</p>
<p><code>createQueryBuilder</code> 方法可以从 <code>DataSource</code> 或 <code>EntityManager</code> 中调用，用于创建特定实体的查询构建器。例如：</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> queryBuilder = AppDataSource.manager.createQueryBuilder(User, <span class="hljs-string">"user"</span>)
</code></pre>
<p>在这个示例中，<code>createQueryBuilder</code> 方法被用来创建一个针对 <code>User</code> 实体的查询构建器，并将该实体命名为 <code>user</code>。这个查询构建器将用于构建和执行查询。</p>
<p><code>QueryBuilder</code> 的方法</p>
<p>一旦你有了 <code>QueryBuilder</code> 实例，就可以使用它提供的各种方法来构建查询。以下是一些常见的方法：</p>
<ol>
<li><p><strong>select</strong>: 选择查询中返回的字段。</p>
<pre><code class="lang-js">queryBuilder.select([<span class="hljs-string">"user.firstName"</span>, <span class="hljs-string">"user.lastName"</span>])
</code></pre>
</li>
<li><p><strong>addSelect</strong>: 添加更多的选择字段。</p>
<pre><code class="lang-js">queryBuilder.addSelect(<span class="hljs-string">"profile.bio"</span>)
</code></pre>
</li>
<li><p><strong>from</strong>: 指定查询的主表。</p>
<pre><code class="lang-js">queryBuilder.from(User, <span class="hljs-string">"user"</span>)
</code></pre>
</li>
<li><p><strong>innerJoin 和 leftJoin</strong>: 添加内部连接和左连接。</p>
<pre><code class="lang-js">queryBuilder.innerJoin(<span class="hljs-string">"user.profile"</span>, <span class="hljs-string">"profile"</span>)
queryBuilder.leftJoin(<span class="hljs-string">"user.profile"</span>, <span class="hljs-string">"profile"</span>)
</code></pre>
</li>
<li><p><strong>where</strong>: 添加查询条件。</p>
<pre><code class="lang-js">queryBuilder.where(<span class="hljs-string">"user.id = :id"</span>, { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span> })
</code></pre>
</li>
<li><p><strong>andWhere 和 orWhere</strong>: 添加更多的查询条件。</p>
<pre><code class="lang-js">queryBuilder.andWhere(<span class="hljs-string">"user.isActive = :isActive"</span>, { <span class="hljs-attr">isActive</span>: <span class="hljs-literal">true</span> })
</code></pre>
</li>
<li><p><strong>getOne 和 getMany</strong>: 执行查询并获取结果。</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> queryBuilder.getOne()
<span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> queryBuilder.getMany()
</code></pre>
</li>
<li><p><strong>delete</strong>: 创建删除查询。</p>
<pre><code class="lang-js">queryBuilder.delete().from(User).where(<span class="hljs-string">"id = :id"</span>, { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span> }).execute()
</code></pre>
</li>
</ol>
<h3 id="t125.2 src\index.ts">5.2 src\index.ts <a href="#t125.2%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-diff">import { AppDataSource } from "./data-source"
import { User } from "./entity/User"
AppDataSource.initialize().then(async () =&gt; {
<span class="hljs-addition">+    // 创建 QueryBuilder 实例</span>
<span class="hljs-addition">+    const queryBuilder = await AppDataSource.manager.createQueryBuilder()</span>
<span class="hljs-addition">+    // 使用 QueryBuilder 插入用户</span>
<span class="hljs-addition">+    await queryBuilder</span>
<span class="hljs-addition">+        .insert()</span>
<span class="hljs-addition">+        .into(User)</span>
<span class="hljs-addition">+        .values([</span>
<span class="hljs-addition">+            { firstName: "John", lastName: "Doe", age: 25, email: "john.doe@example.com", isActive: true }</span>
<span class="hljs-addition">+        ])</span>
<span class="hljs-addition">+        .execute()</span>
<span class="hljs-addition">+    console.log("用户已插入")</span>
<span class="hljs-addition">+    // 使用 QueryBuilder 查找用户</span>
<span class="hljs-addition">+    const users = await queryBuilder</span>
<span class="hljs-addition">+        .select("user")</span>
<span class="hljs-addition">+        .from(User, "user")</span>
<span class="hljs-addition">+        .where("user.firstName = :firstName", { firstName: "John" })</span>
<span class="hljs-addition">+        .getMany()</span>
<span class="hljs-addition">+    console.log("查询到的用户: ", users)</span>
<span class="hljs-addition">+    // 使用 QueryBuilder 更新用户</span>
<span class="hljs-addition">+    await queryBuilder</span>
<span class="hljs-addition">+        .update(User)</span>
<span class="hljs-addition">+        .set({ age: 26 })</span>
<span class="hljs-addition">+        .where("email = :email", { email: "john.doe@example.com" })</span>
<span class="hljs-addition">+        .execute()</span>
<span class="hljs-addition">+    console.log("用户已更新")</span>
<span class="hljs-addition">+    // 使用 QueryBuilder 删除用户</span>
<span class="hljs-addition">+    await queryBuilder</span>
<span class="hljs-addition">+        .delete()</span>
<span class="hljs-addition">+        .from(User)</span>
<span class="hljs-addition">+        .where("email = :email", { email: "john.doe@example.com" })</span>
<span class="hljs-addition">+        .execute()</span>
<span class="hljs-addition">+    console.log("用户已删除")</span>
}).catch(error =&gt; console.log(error))
</code></pre>
<h2 id="t136. getRepository">6. getRepository <a href="#t136.%20getRepository"> # </a></h2>
<ul>
<li><code>getRepository</code> 是 TypeORM 中用于访问和操作实体的主要方法之一。它提供了一组便捷的方法来执行常见的数据库操作，如插入、查找、更新和删除</li>
</ul>
<h3 id="t146.1 src\index.ts">6.1 src\index.ts <a href="#t146.1%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// 获取 User 实体的存储库</span>
    <span class="hljs-keyword">const</span> userRepository = AppDataSource.getRepository(User)
    <span class="hljs-comment">// 插入新用户</span>
    <span class="hljs-keyword">const</span> newUser = <span class="hljs-keyword">new</span> User()
    newUser.firstName = <span class="hljs-string">"John"</span>
    newUser.lastName = <span class="hljs-string">"Doe"</span>
    newUser.age = <span class="hljs-number">25</span>
    newUser.email = <span class="hljs-string">"john.doe@example.com"</span>
    newUser.isActive = <span class="hljs-literal">true</span>
    <span class="hljs-keyword">await</span> userRepository.save(newUser)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户已插入: "</span>, newUser)
    <span class="hljs-comment">// 查找所有用户</span>
    <span class="hljs-keyword">const</span> allUsers = <span class="hljs-keyword">await</span> userRepository.find()
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"所有用户: "</span>, allUsers)
    <span class="hljs-comment">// 查找单个用户</span>
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> userRepository.findOneBy({ <span class="hljs-attr">id</span>: newUser.id })
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"查找到的用户: "</span>, user)
    <span class="hljs-comment">// 更新用户</span>
    <span class="hljs-keyword">if</span> (user) {
        user.age = <span class="hljs-number">26</span>
        <span class="hljs-keyword">await</span> userRepository.save(user)
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户已更新: "</span>, user)
    }
    <span class="hljs-comment">// 删除用户</span>
    <span class="hljs-keyword">if</span> (user) {
        <span class="hljs-keyword">await</span> userRepository.remove(user)
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户已删除: "</span>, user)
    }
    process.exit(<span class="hljs-number">0</span>)
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(error)
    process.exit(<span class="hljs-number">1</span>)
})

</code></pre>
<h2 id="t157.一对一">7.一对一 <a href="#t157.%E4%B8%80%E5%AF%B9%E4%B8%80"> # </a></h2>
<h3 id="t167.1 @OneToOne">7.1 @OneToOne <a href="#t167.1%20@OneToOne"> # </a></h3>
<p><code>@OneToOne</code> 是 TypeORM 中用于定义实体之间一对一关系的装饰器。在一对一关系中，一个实体的每个实例仅与另一个实体的一个实例相关联。这个关系可以是单向的，也可以是双向的。</p>
<ol>
<li><p><strong>基本用法</strong>：
<code>@OneToOne</code> 装饰器用于标记一个实体的属性，表示该属性与另一个实体具有一对一的关系。</p>
</li>
<li><p><strong>参数</strong>：</p>
<ul>
<li>第一个参数是一个函数，用于返回关系的目标实体类型。</li>
<li>第二个参数是一个可选的对象，用于设置关系的选项（如级联操作、关系的所有权等）。</li>
</ul>
</li>
<li><p><strong>选项</strong>：</p>
<ul>
<li><code>cascade</code>: 指定是否级联操作。例如，当保存或删除一个实体时，是否自动保存或删除相关的实体。</li>
<li><code>eager</code>: 指定是否在查询实体时自动加载相关的实体。</li>
<li><code>nullable</code>: 指定该关系是否可以为空。</li>
<li><code>onDelete</code>: 指定当相关实体被删除时应该执行的操作（如 <code>CASCADE</code>, <code>SET NULL</code>）。</li>
</ul>
</li>
</ol>
<h3 id="t177.2 @JoinColumn">7.2 @JoinColumn <a href="#t177.2%20@JoinColumn"> # </a></h3>
<p><code>@JoinColumn</code> 是 TypeORM 中用于定义实体之间关系的一部分注解，特别是在一对一（OneTo-One）、多对一（Many-to-One）和一对多（One-to-Many）关系中，它用于指定关联实体的外键列。</p>
<p>作用：
<code>@JoinColumn</code> 注解用于指定当前实体和关联实体之间关系的连接列。它通常用于定义外键，并且指定了哪个列将作为外键来建立实体之间的关联。</p>
<p>使用场景：</p>
<ul>
<li><strong>一对一关系 (One-to-One)</strong></li>
<li><strong>多对一关系 (Many-to-One)</strong></li>
<li><strong>一对多关系 (One-to-Many)</strong></li>
</ul>
<p>在这些关系中，<code>@JoinColumn</code> 用于明确指定外键列的名称和配置。</p>
<ol>
<li><p><strong>定义关系</strong>：</p>
<ul>
<li>在 <code>User</code> 实体中，通过 <code>@OneToOne</code> 注解定义了与 <code>Profile</code> 实体的一对一关系。</li>
</ul>
</li>
<li><p><strong>使用 @JoinColumn</strong>：</p>
<ul>
<li><code>@JoinColumn</code> 注解放置在 <code>User</code> 实体中的 <code>profile</code> 属性上，表示 <code>User</code> 表中的一个列将作为外键列。</li>
<li>没有指定 <code>@JoinColumn</code> 的参数时，默认的外键列名称将是当前实体属性名加上目标实体的主键列名，比如这里会是 <code>profileId</code>。</li>
</ul>
</li>
<li><p><strong>外键列的生成</strong>：</p>
<ul>
<li>TypeORM 会在 <code>User</code> 表中自动生成一个外键列，默认名称为 <code>profileId</code>，用于存储关联的 <code>Profile</code> 实体的主键值。</li>
</ul>
</li>
<li><p><strong>级联操作</strong>：</p>
<ul>
<li>在 <code>@OneToOne</code> 中，<code>cascade: true</code> 表示在保存 <code>User</code> 时，会自动保存关联的 <code>Profile</code>。</li>
</ul>
</li>
</ol>
<h3 id="t187.3 cascade">7.3 cascade <a href="#t187.3%20cascade"> # </a></h3>
<p><code>cascade</code> 选项用于定义在某些操作（如保存、更新、删除）中是否应该自动应用到相关的实体上。当设置 <code>cascade: true</code> 时，表示在对主实体进行操作时，这些操作也会自动应用到相关的实体上。</p>
<p>示例解释：</p>
<ul>
<li><strong>保存（save）</strong>: 当保存 <code>User</code> 实体时，如果它包含一个新的 <code>Profile</code> 实体，那么 <code>Profile</code> 也会被自动保存。</li>
<li><strong>更新（update）</strong>: 当更新 <code>User</code> 实体时，相关的 <code>Profile</code> 实体也会被自动更新。</li>
<li><strong>删除（remove）</strong>: 当删除 <code>User</code> 实体时，相关的 <code>Profile</code> 实体也会被自动删除。</li>
</ul>
<h3 id="t197.4 onDelete">7.4 onDelete <a href="#t197.4%20onDelete"> # </a></h3>
<p><code>onDelete</code> 选项是一个 SQL 级别的设置，定义了在数据库中当主实体被删除时，相关的外键约束应该执行什么操作。<code>onDelete: "CASCADE"</code> 指定在删除主实体时，相关的外键记录也会被自动删除。</p>
<ul>
<li><code>cascade: true</code> 是一个 TypeORM 级别的选项，管理 ORM 操作时是否自动应用到相关实体上。</li>
<li><code>onDelete: "CASCADE"</code> 和 <code>onUpdate: "CASCADE"</code> 是数据库级别的选项，定义了在数据库中执行删除或更新操作时，外键约束的行为。</li>
</ul>
<p><code>onDelete</code> 是一个 SQL 级别的选项，用于定义在删除主实体时，关联的外键记录应该执行的操作。它可以有多个不同的选项，每个选项指定了不同的行为。</p>
<p>常用 <code>onDelete</code> 选项</p>
<ol>
<li><strong>CASCADE</strong></li>
<li><strong>SET NULL</strong></li>
<li><strong>RESTRICT</strong></li>
<li><strong>NO ACTION</strong></li>
<li><strong>SET DEFAULT</strong></li>
</ol>
<h4 id="t207.4.1. CASCADE">7.4.1. CASCADE <a href="#t207.4.1.%20CASCADE"> # </a></h4>
<p>当主实体被删除时，所有引用该主实体的外键记录也会被自动删除。这种选项适用于当删除一个记录时，也希望删除所有关联的子记录的情况。</p>
<p><strong>示例</strong>：</p>
<ul>
<li>删除一个用户时，自动删除该用户的所有订单。</li>
</ul>
<pre><code class="lang-js">@OneToOne(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> Profile, { <span class="hljs-attr">onDelete</span>: <span class="hljs-string">"CASCADE"</span> })
@JoinColumn()
<span class="hljs-attr">profile</span>: Profile;
</code></pre>
<h4 id="t217.4.2 SET NULL">7.4.2 SET NULL <a href="#t217.4.2%20SET%20NULL"> # </a></h4>
<p>当主实体被删除时，所有引用该主实体的外键记录的外键列将被设置为 <code>NULL</code>。这要求外键列必须允许 <code>NULL</code> 值。</p>
<p><strong>示例</strong>：</p>
<ul>
<li>删除一个用户时，将所有个人资料的用户ID设置为 <code>NULL</code>。</li>
</ul>
<pre><code class="lang-js">@OneToOne(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> Profile, { <span class="hljs-attr">onDelete</span>: <span class="hljs-string">"SET NULL"</span> })
@JoinColumn()
<span class="hljs-attr">profile</span>: Profile;
</code></pre>
<h4 id="t227.4.3  RESTRICT">7.4.3  RESTRICT <a href="#t227.4.3%20%20RESTRICT"> # </a></h4>
<p>当试图删除主实体时，如果有任何关联的外键记录，则会阻止删除操作。这种选项适用于需要保护关联数据不被意外删除的情况。</p>
<p><strong>示例</strong>：</p>
<ul>
<li>尝试删除一个用户时，如果该用户有个人资料，则阻止删除操作。</li>
</ul>
<pre><code class="lang-js">@OneToOne(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> Profile, { <span class="hljs-attr">onDelete</span>: <span class="hljs-string">"RESTRICT"</span> })
@JoinColumn()
<span class="hljs-attr">profile</span>: Profile;
</code></pre>
<h4 id="t237.4.4 NO ACTION">7.4.4 NO ACTION <a href="#t237.4.4%20NO%20ACTION"> # </a></h4>
<p><code>NO ACTION</code> 的行为与 <code>RESTRICT</code> 类似，表示不采取任何行动，实际效果是在一些数据库中与 <code>RESTRICT</code> 相同。在标准 SQL 中，<code>NO ACTION</code> 意味着在外键约束检查后，不采取任何特殊行动。默认情况下，MySQL 会将其视为 <code>RESTRICT</code>。</p>
<p><strong>示例</strong>：</p>
<ul>
<li>删除一个用户时，如果有个人资料关联到该用户，不采取任何操作（阻止删除）。</li>
</ul>
<pre><code class="lang-js">@OneToOne(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> Profile, { <span class="hljs-attr">onDelete</span>: <span class="hljs-string">"NO ACTION"</span> })
@JoinColumn()
<span class="hljs-attr">profile</span>: Profile;
</code></pre>
<h4 id="t247.4.5. SET DEFAULT">7.4.5. SET DEFAULT <a href="#t247.4.5.%20SET%20DEFAULT"> # </a></h4>
<p>当主实体被删除时，所有引用该主实体的外键记录的外键列将被设置为一个默认值。这要求外键列必须有一个默认值。</p>
<p><strong>示例</strong>：</p>
<ul>
<li>删除一个用户时，将所有个人资料的用户ID设置为预设的默认值。</li>
</ul>
<pre><code class="lang-js">@OneToOne(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> Profile, { <span class="hljs-attr">onDelete</span>: <span class="hljs-string">"SET DEFAULT"</span> })
@JoinColumn()
<span class="hljs-attr">profile</span>: Profile;
</code></pre>
<h3 id="t257.5 User.ts">7.5 User.ts <a href="#t257.5%20User.ts"> # </a></h3>
<p>src\entity\User.ts</p>
<pre><code class="lang-diff">import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToOne, JoinColumn } from "typeorm"
import { Profile } from "./Profile"
@Entity()
export class User {
    @PrimaryGeneratedColumn({ comment: "Primary key" })
    id: number
    @Column({ length: 50 })
    firstName: string
    @Column({ length: 50 })
    lastName: string
    @Column({ type: "int" })
    age: number
    @Column({ unique: false, nullable: false })
    email: string
    @Column({ type: "boolean", default: true })
    isActive: boolean
    @CreateDateColumn()
    createdAt: Date
    @UpdateDateColumn()
    updatedAt: Date
<span class="hljs-addition">+   @OneToOne(() =&gt; Profile, profile =&gt; profile.user, { cascade: true })</span>
    profile: Profile
}
</code></pre>
<h3 id="t267.6 Profile.ts">7.6 Profile.ts <a href="#t267.6%20Profile.ts"> # </a></h3>
<p>src\entity\Profile.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./User"</span>
@Entity()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Profile</span> </span>{
    @PrimaryGeneratedColumn({ <span class="hljs-attr">comment</span>: <span class="hljs-string">"Primary key"</span> })
    <span class="hljs-attr">id</span>: number
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span> })
    <span class="hljs-attr">bio</span>: string
    @OneToOne(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> User, user =&gt; user.profile, { <span class="hljs-attr">onDelete</span>: <span class="hljs-string">'CASCADE'</span> })
    @JoinColumn()
    <span class="hljs-attr">user</span>: User
}
</code></pre>
<h3 id="t277.7 data-source.ts">7.7 data-source.ts <a href="#t277.7%20data-source.ts"> # </a></h3>
<p>src\data-source.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">"reflect-metadata"</span>
<span class="hljs-keyword">import</span> { DataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
<span class="hljs-keyword">import</span> { Profile } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/Profile"</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AppDataSource = <span class="hljs-keyword">new</span> DataSource({
    <span class="hljs-attr">type</span>: <span class="hljs-string">"mysql"</span>,
    <span class="hljs-attr">host</span>: <span class="hljs-string">"localhost"</span>,
    <span class="hljs-attr">port</span>: <span class="hljs-number">3306</span>,
    <span class="hljs-attr">username</span>: <span class="hljs-string">"root"</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">"root"</span>,
    <span class="hljs-attr">database</span>: <span class="hljs-string">"orm"</span>,
    <span class="hljs-attr">synchronize</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">logging</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">entities</span>: [User, Profile],
})
</code></pre>
<h3 id="t287.8 src\index.ts">7.8 src\index.ts <a href="#t287.8%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
<span class="hljs-keyword">import</span> { Profile } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/Profile"</span>
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// 创建并保存用户和个人资料</span>
    <span class="hljs-keyword">const</span> newUser = <span class="hljs-keyword">new</span> User()
    newUser.firstName = <span class="hljs-string">"Jane"</span>
    newUser.lastName = <span class="hljs-string">"Doe"</span>
    newUser.age = <span class="hljs-number">28</span>
    newUser.email = <span class="hljs-string">"jane.doe@example.com"</span>
    newUser.isActive = <span class="hljs-literal">true</span>
    <span class="hljs-keyword">const</span> newProfile = <span class="hljs-keyword">new</span> Profile()
    newProfile.bio = <span class="hljs-string">"Jane Doe is a software engineer from San Francisco."</span>
    newUser.profile = newProfile
    <span class="hljs-keyword">await</span> AppDataSource.manager.save(newUser)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户和个人资料已保存: "</span>, newUser)
    <span class="hljs-comment">// 查找用户及其个人资料</span>
    <span class="hljs-keyword">const</span> savedUser = <span class="hljs-keyword">await</span> AppDataSource.manager.findOne(User, {
        <span class="hljs-attr">where</span>: { <span class="hljs-attr">id</span>: newUser.id },
        <span class="hljs-attr">relations</span>: [<span class="hljs-string">"profile"</span>],
    })
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"查询到的用户及其个人资料: "</span>, savedUser)
    <span class="hljs-comment">// 更新用户的个人资料</span>
    <span class="hljs-keyword">if</span> (savedUser &amp;&amp; savedUser.profile) {
        savedUser.profile.bio = <span class="hljs-string">"Jane Doe is a senior software engineer from San Francisco."</span>
        <span class="hljs-keyword">await</span> AppDataSource.manager.save(savedUser.profile)
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"个人资料已更新: "</span>, savedUser.profile)
    }
    <span class="hljs-comment">// 删除用户及其个人资料</span>
    <span class="hljs-keyword">if</span> (savedUser) {
        <span class="hljs-keyword">await</span> AppDataSource.manager.remove(savedUser)
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户及其个人资料已删除"</span>)
    }
    process.exit(<span class="hljs-number">0</span>)
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(error)
    process.exit(<span class="hljs-number">1</span>)
})
</code></pre>
<h3 id="t297.9 一对一queryBuilder">7.9 一对一queryBuilder <a href="#t297.9%20%E4%B8%80%E5%AF%B9%E4%B8%80queryBuilder"> # </a></h3>
<h4 id="t307.9.1 src\index.ts">7.9.1 src\index.ts <a href="#t307.9.1%20src\index.ts"> # </a></h4>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
<span class="hljs-keyword">import</span> { Profile } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/Profile"</span>
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// 创建并保存用户和个人资料</span>
    <span class="hljs-keyword">const</span> newUser = <span class="hljs-keyword">new</span> User()
    newUser.firstName = <span class="hljs-string">"Jane"</span>
    newUser.lastName = <span class="hljs-string">"Doe"</span>
    newUser.age = <span class="hljs-number">28</span>
    newUser.email = <span class="hljs-string">"jane.doe@example.com"</span>
    newUser.isActive = <span class="hljs-literal">true</span>
    <span class="hljs-keyword">const</span> newProfile = <span class="hljs-keyword">new</span> Profile()
    newProfile.bio = <span class="hljs-string">"Jane Doe is a software engineer from San Francisco."</span>
    newUser.profile = newProfile
    <span class="hljs-keyword">await</span> AppDataSource.manager.save(newUser)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户和个人资料已保存: "</span>, newUser)
    <span class="hljs-comment">// 使用 createQueryBuilder 查找用户及其个人资料</span>
    <span class="hljs-keyword">const</span> queryBuilder = AppDataSource.manager.createQueryBuilder(User, <span class="hljs-string">"user"</span>)
    <span class="hljs-keyword">const</span> savedUser = <span class="hljs-keyword">await</span> queryBuilder
        .leftJoinAndSelect(<span class="hljs-string">"user.profile"</span>, <span class="hljs-string">"profile"</span>)
        .where(<span class="hljs-string">"user.id = :id"</span>, { <span class="hljs-attr">id</span>: newUser.id })
        .getOne()
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"查询到的用户及其个人资料: "</span>, savedUser)
    <span class="hljs-comment">// 更新用户的个人资料</span>
    <span class="hljs-keyword">if</span> (savedUser &amp;&amp; savedUser.profile) {
        savedUser.profile.bio = <span class="hljs-string">"Jane Doe is a senior software engineer from San Francisco."</span>
        <span class="hljs-keyword">await</span> AppDataSource.manager.save(savedUser.profile)
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"个人资料已更新: "</span>, savedUser.profile)
    }
    <span class="hljs-comment">// 使用 createQueryBuilder 删除用户及其个人资料</span>
    <span class="hljs-keyword">if</span> (savedUser) {
        <span class="hljs-keyword">await</span> AppDataSource.manager.createQueryBuilder()
            .delete()
            .from(User)
            .where(<span class="hljs-string">"id = :id"</span>, { <span class="hljs-attr">id</span>: savedUser.id })
            .execute()
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"用户及其个人资料已删除"</span>)
    }
    process.exit(<span class="hljs-number">0</span>)
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(error)
    process.exit(<span class="hljs-number">1</span>)
}).finally(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    process.exit(<span class="hljs-number">1</span>)
})
</code></pre>
<h2 id="t318.一对多和多对一">8.一对多和多对一 <a href="#t318.%E4%B8%80%E5%AF%B9%E5%A4%9A%E5%92%8C%E5%A4%9A%E5%AF%B9%E4%B8%80"> # </a></h2>
<h3 id="t328.1 @OneToMany">8.1 @OneToMany <a href="#t328.1%20@OneToMany"> # </a></h3>
<p>基本概念</p>
<ul>
<li><strong>一对多关系</strong>：一个实体可以拥有多个关联的实体。例如，一个用户可以有多个帖子。这就是用户（User）和帖子（Post）之间的一对多关系。</li>
<li><strong>实体装饰器</strong>：在 TypeORM 中，实体（entity）是数据库表的映射。通过使用装饰器（decorator），我们可以定义实体之间的关系。</li>
</ul>
<p>定义一对多关系
在一对多关系中，我们需要在“一”方（例如用户）使用 @OneToMany 装饰器，同时在“多”方（例如帖子）使用 @ManyToOne 装饰器。</p>
<ol>
<li><p><strong>User 实体</strong>：</p>
<ul>
<li><code>@OneToMany(() =&gt; Post, post =&gt; post.user)</code>：<ul>
<li><code>()</code> =&gt; Post`：这是关系的目标实体类型。</li>
<li><code>post =&gt; post.user</code>：这是一个函数，表示 Post 实体的 <code>user</code> 属性是关系的一部分。</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Post 实体</strong>：</p>
<ul>
<li><code>@ManyToOne(() =&gt; User, user =&gt; user.posts)</code>：<ul>
<li><code>() =&gt; User</code>：这是关系的目标实体类型。</li>
<li><code>user =&gt; user.posts</code>：这是一个函数，表示 User 实体的 <code>posts</code> 属性是关系的一部分。</li>
</ul>
</li>
</ul>
</li>
</ol>
<h3 id="t338.2 @ManyToOne">8.2 @ManyToOne <a href="#t338.2%20@ManyToOne"> # </a></h3>
<p><code>@ManyToOne</code> 是 TypeORM 中用于定义多对一关系的装饰器。</p>
<p>基本概念</p>
<ul>
<li><strong>多对一关系</strong>：在多对一关系中，多个实体可以关联到同一个实体。例如，多篇帖子可以关联到同一个用户。这就是帖子（Post）和用户（User）之间的多对一关系。</li>
<li><strong>实体装饰器</strong>：在 TypeORM 中，实体（entity）是数据库表的映射。通过使用装饰器（decorator），我们可以定义实体之间的关系。</li>
</ul>
<p>在多对一关系中，我们需要在“多”方（例如帖子）使用 <code>@ManyToOne</code> 装饰器，同时在“一”方（例如用户）使用 <code>@OneToMany</code> 装饰器。</p>
<h3 id="t348.3 User.ts">8.3 User.ts <a href="#t348.3%20User.ts"> # </a></h3>
<p>src\entity\User.ts</p>
<pre><code class="lang-js"><span class="hljs-comment">// 导入必要的模块和类</span>
<span class="hljs-keyword">import</span> { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-comment">// 导入 Order 实体</span>
<span class="hljs-keyword">import</span> { Order } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Order"</span>
<span class="hljs-comment">// 定义 User 实体类</span>
@Entity()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-comment">// 主键字段，自动生成</span>
    @PrimaryGeneratedColumn({ <span class="hljs-attr">comment</span>: <span class="hljs-string">"Primary key"</span> })
    <span class="hljs-attr">id</span>: number
    <span class="hljs-comment">// 名字字段，长度为 50</span>
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">50</span> })
    <span class="hljs-attr">firstName</span>: string
    <span class="hljs-comment">// 姓氏字段，长度为 50</span>
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">50</span> })
    <span class="hljs-attr">lastName</span>: string
    <span class="hljs-comment">// 年龄字段，类型为整数</span>
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"int"</span> })
    <span class="hljs-attr">age</span>: number
    <span class="hljs-comment">// 邮箱字段，非唯一，不能为空</span>
    @Column({ <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">nullable</span>: <span class="hljs-literal">false</span> })
    <span class="hljs-attr">email</span>: string
    <span class="hljs-comment">// 活跃状态字段，类型为布尔值，默认值为 true</span>
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"boolean"</span>, <span class="hljs-attr">default</span>: <span class="hljs-literal">true</span> })
    <span class="hljs-attr">isActive</span>: boolean
    <span class="hljs-comment">// 创建日期字段，自动生成</span>
    @CreateDateColumn()
    <span class="hljs-attr">createdAt</span>: <span class="hljs-built_in">Date</span>
    <span class="hljs-comment">// 更新日期字段，自动生成</span>
    @UpdateDateColumn()
    <span class="hljs-attr">updatedAt</span>: <span class="hljs-built_in">Date</span>
    <span class="hljs-comment">// 一对多关系定义，关联到 Order 实体</span>
    @OneToMany(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> Order, order =&gt; order.user)
    <span class="hljs-attr">orders</span>: Order[]
}
</code></pre>
<h3 id="t358.4 Order.ts">8.4 Order.ts <a href="#t358.4%20Order.ts"> # </a></h3>
<p>src\entity\Order.ts</p>
<pre><code class="lang-js"><span class="hljs-comment">// 导入必要的模块和类</span>
<span class="hljs-keyword">import</span> { Entity, PrimaryGeneratedColumn, Column, ManyToOne } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-comment">// 导入 User 实体</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./User"</span>
<span class="hljs-comment">// 定义 Order 实体类</span>
@Entity()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Order</span> </span>{
    <span class="hljs-comment">// 主键字段，自动生成</span>
    @PrimaryGeneratedColumn({ <span class="hljs-attr">comment</span>: <span class="hljs-string">"Primary key"</span> })
    <span class="hljs-attr">id</span>: number
    <span class="hljs-comment">// 产品名称字段，长度为 100</span>
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">100</span> })
    <span class="hljs-attr">product</span>: string
    <span class="hljs-comment">// 数量字段，类型为整数</span>
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"int"</span> })
    <span class="hljs-attr">amount</span>: number
    <span class="hljs-comment">// 多对一关系定义，关联到 User 实体</span>
    @ManyToOne(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> User, user =&gt; user.orders)
    <span class="hljs-attr">user</span>: User
}
</code></pre>
<h3 id="t368.5  src\index.ts">8.5  src\index.ts <a href="#t368.5%20%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-comment">// 导入必要的数据源和实体类</span>
<span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
<span class="hljs-keyword">import</span> { Order } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/Order"</span>
<span class="hljs-comment">// 初始化数据源并执行数据库操作</span>
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// 创建一个新的用户实例</span>
    <span class="hljs-keyword">const</span> user1 = <span class="hljs-keyword">new</span> User()
    user1.firstName = <span class="hljs-string">"John"</span>
    user1.lastName = <span class="hljs-string">"Doe"</span>
    user1.age = <span class="hljs-number">25</span>
    user1.email = <span class="hljs-string">"john.doe@example.com"</span>
    user1.isActive = <span class="hljs-literal">true</span>
    <span class="hljs-comment">// 保存用户到数据库</span>
    <span class="hljs-keyword">await</span> AppDataSource.manager.save(user1)
    <span class="hljs-comment">// 创建第一个订单并关联到用户</span>
    <span class="hljs-keyword">const</span> order1 = <span class="hljs-keyword">new</span> Order()
    order1.product = <span class="hljs-string">"Product A"</span>
    order1.amount = <span class="hljs-number">2</span>
    order1.user = user1
    <span class="hljs-comment">// 保存第一个订单到数据库</span>
    <span class="hljs-keyword">await</span> AppDataSource.manager.save(order1)
    <span class="hljs-comment">// 创建第二个订单并关联到用户</span>
    <span class="hljs-keyword">const</span> order2 = <span class="hljs-keyword">new</span> Order()
    order2.product = <span class="hljs-string">"Product B"</span>
    order2.amount = <span class="hljs-number">3</span>
    order2.user = user1
    <span class="hljs-comment">// 保存第二个订单到数据库</span>
    <span class="hljs-keyword">await</span> AppDataSource.manager.save(order2)
    <span class="hljs-comment">// 创建查询构建器，用于查询用户及其订单总金额</span>
    <span class="hljs-keyword">const</span> queryBuilder = AppDataSource.manager.createQueryBuilder(User, <span class="hljs-string">"user"</span>)
    <span class="hljs-keyword">const</span> query = queryBuilder
        .select([<span class="hljs-string">'user.firstName'</span>, <span class="hljs-string">'user.lastName'</span>]) <span class="hljs-comment">// 选择用户的名字和姓氏</span>
        .addSelect(<span class="hljs-string">'SUM(order.amount)'</span>, <span class="hljs-string">'totalAmount'</span>) <span class="hljs-comment">// 选择订单总金额</span>
        .innerJoin(<span class="hljs-string">'user.orders'</span>, <span class="hljs-string">'order'</span>) <span class="hljs-comment">// 内连接用户和订单</span>
        .where(<span class="hljs-string">'user.isActive = :isActive'</span>, { <span class="hljs-attr">isActive</span>: <span class="hljs-literal">true</span> }) <span class="hljs-comment">// 筛选活跃用户</span>
        .andWhere(<span class="hljs-string">'order.product = :product'</span>, { <span class="hljs-attr">product</span>: <span class="hljs-string">'Product A'</span> }) <span class="hljs-comment">// 筛选产品为 Product A 的订单</span>
        .groupBy(<span class="hljs-string">'user.id'</span>) <span class="hljs-comment">// 按用户 ID 分组</span>
        .orderBy(<span class="hljs-string">'totalAmount'</span>, <span class="hljs-string">'DESC'</span>) <span class="hljs-comment">// 按订单总金额降序排序</span>
    <span class="hljs-comment">// 执行查询并获取结果</span>
    <span class="hljs-keyword">const</span> results = <span class="hljs-keyword">await</span> query.getRawMany()
    <span class="hljs-comment">// 输出查询结果</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"查询到的用户及其订单总金额: "</span>, results)
    <span class="hljs-comment">// 退出进程</span>
    process.exit(<span class="hljs-number">0</span>)
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
    <span class="hljs-comment">// 输出错误信息并退出进程</span>
    <span class="hljs-built_in">console</span>.log(error)
    process.exit(<span class="hljs-number">1</span>)
})
</code></pre>
<h2 id="t379.多对多">9.多对多 <a href="#t379.%E5%A4%9A%E5%AF%B9%E5%A4%9A"> # </a></h2>
<h3 id="t389.1 @ManyToMany">9.1 @ManyToMany <a href="#t389.1%20@ManyToMany"> # </a></h3>
<p><code>@ManyToMany</code> 是 TypeORM 中用于定义实体之间多对多关系的装饰器。在多对多关系中，一个实体可以与多个另一个实体关联，反之亦然。例如，一个用户可以有多个角色，一个角色也可以分配给多个用户。</p>
<p><strong>1. 基本语法</strong></p>
<pre><code class="lang-js">@ManyToMany(<span class="hljs-function"><span class="hljs-params">type</span> =&gt;</span> AnotherEntity, anotherEntity =&gt; anotherEntity.property)
</code></pre>
<ul>
<li><code>type =&gt; AnotherEntity</code>：一个函数，返回要关联的实体类。在这个例子中是 <code>Role</code>。</li>
<li><code>anotherEntity =&gt; anotherEntity.property</code>：另一个函数，指定关联实体的哪个属性定义了这个关系。在这个例子中是 <code>Role</code> 类中的 <code>users</code> 属性。</li>
</ul>
<h3 id="t399.2 @JoinTable">9.2 @JoinTable <a href="#t399.2%20@JoinTable"> # </a></h3>
<p>在多对多关系中，至少一侧需要使用 <code>@JoinTable</code> 装饰器。这个装饰器表示这个实体是关系的所有者，并且会在数据库中创建一个联结表，用于保存关联信息。</p>
<p>在我们的示例中，<code>@JoinTable</code> 放在 <code>User</code> 实体的 <code>roles</code> 属性上，这意味着 <code>User</code> 实体是关系的所有者，联结表将保存用户和角色之间的关系。</p>
<h3 id="t409.3 User.ts">9.3 User.ts <a href="#t409.3%20User.ts"> # </a></h3>
<p>src\entity\User.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToMany, JoinTable } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-keyword">import</span> { Role } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Role"</span>
@Entity()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    @PrimaryGeneratedColumn({ <span class="hljs-attr">comment</span>: <span class="hljs-string">"Primary key"</span> })
    <span class="hljs-attr">id</span>: number
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">50</span> })
    <span class="hljs-attr">firstName</span>: string
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">50</span> })
    <span class="hljs-attr">lastName</span>: string
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"int"</span> })
    <span class="hljs-attr">age</span>: number
    @Column({ <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">nullable</span>: <span class="hljs-literal">false</span> })
    <span class="hljs-attr">email</span>: string
    @Column({ <span class="hljs-attr">type</span>: <span class="hljs-string">"boolean"</span>, <span class="hljs-attr">default</span>: <span class="hljs-literal">true</span> })
    <span class="hljs-attr">isActive</span>: boolean
    @CreateDateColumn()
    <span class="hljs-attr">createdAt</span>: <span class="hljs-built_in">Date</span>
    @UpdateDateColumn()
    <span class="hljs-attr">updatedAt</span>: <span class="hljs-built_in">Date</span>
    @ManyToMany(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> Role, role =&gt; role.users)
    @JoinTable()
    <span class="hljs-attr">roles</span>: Role[]
}
</code></pre>
<h3 id="t419.4 Role.ts">9.4 Role.ts <a href="#t419.4%20Role.ts"> # </a></h3>
<p>src\entity\Role.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Entity, PrimaryGeneratedColumn, Column, ManyToMany } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./User"</span>
@Entity()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Role</span> </span>{
    @PrimaryGeneratedColumn({ <span class="hljs-attr">comment</span>: <span class="hljs-string">"Primary key"</span> })
    <span class="hljs-attr">id</span>: number
    @Column({ <span class="hljs-attr">length</span>: <span class="hljs-number">50</span> })
    <span class="hljs-attr">name</span>: string
    @ManyToMany(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> User, user =&gt; user.roles)
    <span class="hljs-attr">users</span>: User[]
}
</code></pre>
<h3 id="t429.5 index.ts">9.5 index.ts <a href="#t429.5%20index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/User"</span>
<span class="hljs-keyword">import</span> { Role } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/Role"</span>
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> userRepository = AppDataSource.getRepository(User)
    <span class="hljs-keyword">const</span> roleRepository = AppDataSource.getRepository(Role)
    <span class="hljs-comment">// 创建新的角色</span>
    <span class="hljs-keyword">const</span> role1 = <span class="hljs-keyword">new</span> Role()
    role1.name = <span class="hljs-string">"Admin"</span>
    <span class="hljs-keyword">await</span> roleRepository.save(role1)
    <span class="hljs-keyword">const</span> role2 = <span class="hljs-keyword">new</span> Role()
    role2.name = <span class="hljs-string">"User"</span>
    <span class="hljs-keyword">await</span> roleRepository.save(role2)
    <span class="hljs-comment">// 创建新的用户</span>
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">new</span> User()
    user.firstName = <span class="hljs-string">"John"</span>
    user.lastName = <span class="hljs-string">"Doe"</span>
    user.age = <span class="hljs-number">30</span>
    user.email = <span class="hljs-string">"john.doe@example.com"</span>
    user.roles = [role1, role2] <span class="hljs-comment">// 分配角色</span>
    <span class="hljs-keyword">await</span> userRepository.save(user)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"User has been saved with roles: "</span>, user)
    process.exit(<span class="hljs-number">0</span>)
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(error)
    process.exit(<span class="hljs-number">1</span>)
})
</code></pre>
<h2 id="t4310.树型结构">10.树型结构 <a href="#t4310.%E6%A0%91%E5%9E%8B%E7%BB%93%E6%9E%84"> # </a></h2>
<p><code>@Tree</code> 是 TypeORM 中用于定义树形结构实体的装饰器。树形结构常用于表示层级关系的数据，例如文件目录、组织结构、分类等。TypeORM 提供了多种树形结构来处理不同的场景，如闭包表、嵌套集、物化路径和父子关系。</p>
<ul>
<li><code>@Tree</code>：定义树形结构类型。支持 <code>closure-table</code>、<code>nested-set</code>、<code>materialized-path</code> 和 <code>adjacency-list</code>。</li>
<li><code>@TreeChildren</code>：定义子节点的属性。</li>
<li><code>@TreeParent</code>：定义父节点的属性。</li>
</ul>
<table>
<thead>
<tr>
<th>方法名</th>
<th>介绍</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>find</code></td>
<td>查询所有目录（或其他条件下的所有实体）。</td>
</tr>
<tr>
<td><code>findTrees</code></td>
<td>查询整个树结构，包含所有层级的目录。</td>
</tr>
<tr>
<td><code>findRoots</code></td>
<td>查询所有根目录。</td>
</tr>
<tr>
<td><code>findAncestorsTree</code></td>
<td>查询特定节点的祖先树，返回包含该节点祖先的完整树结构。</td>
</tr>
<tr>
<td><code>findAncestors</code></td>
<td>查询特定节点的所有祖先节点。</td>
</tr>
<tr>
<td><code>findDescendantsTree</code></td>
<td>查询特定节点的后代树，返回包含该节点后代的完整树结构。</td>
</tr>
<tr>
<td><code>findDescendants</code></td>
<td>查询特定节点的所有后代节点。</td>
</tr>
<tr>
<td><code>countDescendants</code></td>
<td>统计特定节点的后代数量。</td>
</tr>
<tr>
<td><code>countAncestors</code></td>
<td>统计特定节点的祖先数量。</td>
</tr>
</tbody>
</table>
<h3 id="t4410.1 树形结构类型">10.1 树形结构类型 <a href="#t4410.1%20%E6%A0%91%E5%BD%A2%E7%BB%93%E6%9E%84%E7%B1%BB%E5%9E%8B"> # </a></h3>
<h4 id="t4510.1.1 adjacency-list">10.1.1 adjacency-list <a href="#t4510.1.1%20adjacency-list"> # </a></h4>
<p><code>adjacency-list</code> 是一种表示树形结构的简单且直观的方法。在这种模型中，每个节点记录其直接父节点的信息，通过这种方式可以表示出层级关系。这种方法实现简单，适合插入和删除操作频繁的场景。</p>
<p>在 <code>adjacency-list</code> 模型中，每个节点有一个引用，指向其父节点。通过这种引用，可以构建出树形结构。</p>
<p><strong>优点：</strong></p>
<ul>
<li>实现简单，易于理解。</li>
<li>插入和删除操作高效，只需更新少量节点。</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>查询效率低。特别是查询整个树结构或计算某节点的所有祖先或后代时，需要多次递归查询。</li>
<li>适合层级关系较浅的数据结构，不适合层级较深且需要频繁查询的场景。</li>
</ul>
<h4 id="t4610.1.2 nested-set">10.1.2 nested-set <a href="#t4610.1.2%20nested-set"> # </a></h4>
<p><code>nested-set</code> 是一种表示树形结构的技术，通过在节点上存储左右值来表示层级关系。这种方法非常适合进行层级数据的查询，特别是在需要频繁读取层级数据的情况下。
在 <code>nested-set</code> 模型中，每个节点都有两个额外的属性：<code>left</code> 和 <code>right</code>。这些属性表示节点在树中的位置。具体来说：</p>
<ul>
<li><code>left</code>：节点的左值，表示进入节点的边界。</li>
<li><code>right</code>：节点的右值，表示离开节点的边界。</li>
</ul>
<p>树的根节点的 <code>left</code> 是 1，<code>right</code> 是树中最大值。每个节点的 <code>left</code> 和 <code>right</code> 值都大于其所有子节点的 <code>left</code> 和 <code>right</code> 值。</p>
<ol start="2">
<li>优缺点</li>
</ol>
<p><strong>优点：</strong></p>
<ul>
<li>查询效率高。可以通过简单的条件查询获取整个子树或特定节点的所有祖先。</li>
<li>适合需要频繁读取层级数据的场景。</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>插入和删除操作复杂。需要重新计算并更新相关节点的 <code>left</code> 和 <code>right</code> 值。</li>
<li>更新操作会导致大量的写操作。</li>
</ul>
<h4 id="t4710.1.3 closure-table">10.1.3 closure-table <a href="#t4710.1.3%20closure-table"> # </a></h4>
<p><code>closure-table</code> 是一种表示树形结构的技术，通过维护一个额外的闭包表来存储每个节点与其祖先的关系。这种方法适合进行复杂的层级查询，同时保证插入和删除操作的效率。</p>
<p><strong>优点：</strong></p>
<ul>
<li>查询效率高。特别适合需要进行复杂层级查询的场景，如获取所有祖先或后代。</li>
<li>插入和删除操作相对简单。插入节点时，只需要更新相关的闭包表。</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>需要维护额外的闭包表，会增加存储开销。</li>
<li>更新操作会涉及到闭包表的多次插入和删除。</li>
</ul>
<h4 id="t4810.1.4 materialized-path">10.1.4 materialized-path <a href="#t4810.1.4%20materialized-path"> # </a></h4>
<p><code>materialized-path</code> 是一种表示树形结构的方法，通过在节点上存储路径字符串来表示层级关系。这种方法适合进行插入和删除操作频繁的场景，路径字符串可以快速确定节点的祖先和后代。</p>
<p>基本概念</p>
<p>在 <code>materialized-path</code> 模型中，每个节点都有一个额外的属性 <code>path</code>，表示从根节点到当前节点的路径。这个路径通常是通过连接每个节点的 ID 来形成的，例如用斜杠（<code>/</code>）分隔。</p>
<p><strong>优点：</strong></p>
<ul>
<li>插入和删除操作简单。只需要更新当前节点及其后代的路径属性。</li>
<li>可以通过简单的字符串匹配查询获取祖先或后代节点。</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>查询深层次的树结构效率较低。</li>
<li>需要额外存储路径信息，增加了一定的存储开销。</li>
</ul>
<h3 id="t4910.2 Category.ts">10.2 Category.ts <a href="#t4910.2%20Category.ts"> # </a></h3>
<p>src\entity\Category.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Entity, Tree, TreeChildren, TreeParent, PrimaryGeneratedColumn, Column } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>
@Entity()
@Tree(<span class="hljs-string">"closure-table"</span>)
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Category</span> </span>{
    @PrimaryGeneratedColumn()
    <span class="hljs-attr">id</span>: number
    @Column()
    <span class="hljs-attr">name</span>: string
    @TreeChildren()
    <span class="hljs-attr">children</span>: Category[]
    @TreeParent()
    <span class="hljs-attr">parent</span>: Category
}
</code></pre>
<h3 id="t5010.3 src\index.ts">10.3 src\index.ts <a href="#t5010.3%20src\index.ts"> # </a></h3>
<p>src\index.ts</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { AppDataSource } <span class="hljs-keyword">from</span> <span class="hljs-string">"./data-source"</span>
<span class="hljs-keyword">import</span> { Category } <span class="hljs-keyword">from</span> <span class="hljs-string">"./entity/Category"</span>;
AppDataSource.initialize().then(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> categoryRepository = AppDataSource.getTreeRepository(Category)
    <span class="hljs-comment">// 创建根目录</span>
    <span class="hljs-keyword">const</span> root = <span class="hljs-keyword">new</span> Category()
    root.name = <span class="hljs-string">"Root"</span>
    <span class="hljs-keyword">await</span> categoryRepository.save(root)
    <span class="hljs-comment">// 创建子目录</span>
    <span class="hljs-keyword">const</span> child1 = <span class="hljs-keyword">new</span> Category()
    child1.name = <span class="hljs-string">"Child 1"</span>
    child1.parent = root
    <span class="hljs-keyword">await</span> categoryRepository.save(child1)
    <span class="hljs-keyword">const</span> child2 = <span class="hljs-keyword">new</span> Category()
    child2.name = <span class="hljs-string">"Child 2"</span>
    child2.parent = root
    <span class="hljs-keyword">await</span> categoryRepository.save(child2)
    <span class="hljs-keyword">const</span> grandChild = <span class="hljs-keyword">new</span> Category()
    grandChild.name = <span class="hljs-string">"Grandchild"</span>
    grandChild.parent = child1
    <span class="hljs-keyword">await</span> categoryRepository.save(grandChild)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Categories have been saved"</span>)
    <span class="hljs-comment">// 查询所有目录</span>
    <span class="hljs-keyword">const</span> allCategories = <span class="hljs-keyword">await</span> categoryRepository.find()
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"All categories: "</span>, <span class="hljs-built_in">JSON</span>.stringify(allCategories, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>))
    <span class="hljs-comment">// 查询树结构</span>
    <span class="hljs-keyword">const</span> trees = <span class="hljs-keyword">await</span> categoryRepository.findTrees()
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Tree structure: "</span>, <span class="hljs-built_in">JSON</span>.stringify(trees, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>))
    <span class="hljs-comment">// 查询根目录</span>
    <span class="hljs-keyword">const</span> roots = <span class="hljs-keyword">await</span> categoryRepository.findRoots()
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Root categories: "</span>, <span class="hljs-built_in">JSON</span>.stringify(roots, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>))
    <span class="hljs-comment">// 查询祖先树</span>
    <span class="hljs-keyword">const</span> ancestorsTree = <span class="hljs-keyword">await</span> categoryRepository.findAncestorsTree(grandChild)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Ancestors tree of Grandchild: "</span>, <span class="hljs-built_in">JSON</span>.stringify(ancestorsTree, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>))
    <span class="hljs-comment">// 查询祖先</span>
    <span class="hljs-keyword">const</span> ancestors = <span class="hljs-keyword">await</span> categoryRepository.findAncestors(grandChild)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Ancestors of Grandchild: "</span>, <span class="hljs-built_in">JSON</span>.stringify(ancestors, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>))
    <span class="hljs-comment">// 查询后代树</span>
    <span class="hljs-keyword">const</span> descendantsTree = <span class="hljs-keyword">await</span> categoryRepository.findDescendantsTree(root)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Descendants tree of Root: "</span>, <span class="hljs-built_in">JSON</span>.stringify(descendantsTree, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>))
    <span class="hljs-comment">// 查询后代</span>
    <span class="hljs-keyword">const</span> descendants = <span class="hljs-keyword">await</span> categoryRepository.findDescendants(root)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Descendants of Root: "</span>, <span class="hljs-built_in">JSON</span>.stringify(descendants, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>))
    <span class="hljs-comment">// 统计后代数量</span>
    <span class="hljs-keyword">const</span> countDescendants = <span class="hljs-keyword">await</span> categoryRepository.countDescendants(root)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Number of descendants of Root: "</span>, countDescendants)
    <span class="hljs-comment">// 统计祖先数量</span>
    <span class="hljs-keyword">const</span> countAncestors = <span class="hljs-keyword">await</span> categoryRepository.countAncestors(grandChild)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Number of ancestors of Grandchild: "</span>, countAncestors)
}).catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(error)
    process.exit(<span class="hljs-number">1</span>)
}).finally(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> process.exit(<span class="hljs-number">0</span>))
</code></pre>
<h2 id="t5111.数据库迁移">11.数据库迁移 <a href="#t5111.%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%81%E7%A7%BB"> # </a></h2>
<ul>
<li><a href="https://typeorm.io/migrations">migrations</a></li>
</ul>
<p>TypeORM 的迁移（Migration）功能允许你在数据库架构变化时有条理地管理和应用这些变化。迁移是开发和生产环境中保持数据库结构同步的一种可靠方法。</p>
<p>迁移是指数据库架构的版本控制。它们允许你：</p>
<ul>
<li>定义数据库结构的变化（如创建或删除表、添加或修改列）。</li>
<li>将这些变化应用到数据库中。</li>
<li>维护一个有序的数据库版本历史，以便你可以轻松地迁移到特定的版本或回滚到以前的版本。</li>
</ul>
<pre><code class="lang-js">npm install ts-node typescript typeorm -g
</code></pre>
<h3 id="t5211.1 migration:create">11.1 migration:create <a href="#t5211.1%20migration:create"> # </a></h3>
<p><code>migration:create</code> 是 TypeORM 提供的一个命令，用于创建一个新的迁移文件。迁移文件包含用于数据库结构变更的代码，例如创建或修改表、添加或删除列等。这在数据库 schema 的版本控制和演进中非常有用。</p>
<p>主要特点和作用</p>
<ol>
<li><strong>创建迁移文件</strong>：生成一个包含基本模板的迁移文件，你可以在其中添加具体的数据库变更逻辑。</li>
<li><strong>组织变更</strong>：迁移文件使得数据库变更井然有序，易于管理和版本控制。</li>
<li><strong>协作开发</strong>：多个开发者可以分别创建和提交迁移文件，确保数据库变更不会相互冲突。</li>
</ol>
<p>使用方法</p>
<p>在项目的根目录下运行以下命令，创建一个新的迁移文件：</p>
<pre><code class="lang-js">npx ts-node ./node_modules/typeorm/cli  migration:create ./src/migrations/init
</code></pre>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { MigrationInterface, QueryRunner } <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NameOfYourMigration1653428384621</span> <span class="hljs-title">implements</span> <span class="hljs-title">MigrationInterface</span> </span>{
  public <span class="hljs-keyword">async</span> up(queryRunner: QueryRunner): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt; {
    <span class="hljs-comment">// 在这里编写升级数据库的变更逻辑，例如创建表、添加列等</span>
    <span class="hljs-keyword">await</span> queryRunner.query(<span class="hljs-string">``</span>);
  }
  public <span class="hljs-keyword">async</span> down(queryRunner: QueryRunner): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt; {
    <span class="hljs-comment">// 在这里编写回滚数据库的变更逻辑，撤销 `up` 方法中的操作</span>
    <span class="hljs-keyword">await</span> queryRunner.query(<span class="hljs-string">``</span>);
  }
}
</code></pre>
<ul>
<li><code>up</code> 方法：定义应用迁移时执行的操作。</li>
<li><code>down</code> 方法：定义回滚迁移时执行的操作。</li>
</ul>
<h3 id="t5311.2 migration:generate">11.2 migration:generate <a href="#t5311.2%20migration:generate"> # </a></h3>
<p><code>migration:generate</code> 是 TypeORM CLI 提供的一个命令，用于根据当前实体（Entities）和数据库结构之间的差异自动生成迁移文件。这个命令极大地简化了开发过程中的数据库迁移管理工作。</p>
<ul>
<li><code>migration:generate</code> 命令用于自动生成迁移文件，反映实体和数据库之间的变化。</li>
<li>生成的迁移文件包含 <code>up</code> 和 <code>down</code> 方法，分别用于应用和回滚迁移。</li>
<li>使用 <code>migration:generate</code> 命令之前，确保实体定义和数据源配置正确。</li>
<li>运行生成的迁移文件以应用数据库结构的变化。</li>
</ul>
<pre><code class="lang-js">npx ts-node ./node_modules/typeorm/cli  migration:generate ./src/migrations/init -d ./src/data-source.ts
</code></pre>
<h3 id="t5411.3 migration:run">11.3 migration:run <a href="#t5411.3%20migration:run"> # </a></h3>
<p><code>migration:run</code> 是 TypeORM 提供的一个命令，用于运行所有未执行的迁移文件，将数据库结构更新到最新的状态。这个命令对于保持数据库模式与应用程序代码之间的一致性非常重要，特别是在团队协作和生产环境中。</p>
<ul>
<li><code>migration:run</code> 命令用于运行所有未执行的迁移文件，以确保数据库结构与应用程序代码保持一致。</li>
<li>在运行 <code>migration:run</code> 命令之前，确保 TypeORM CLI 能够正确找到数据源配置。</li>
<li>可以通过配置文件、数据源文件或使用 <code>ts-node</code> 运行 TypeScript 文件来配置 TypeORM CLI。</li>
</ul>
<pre><code class="lang-js">npx ts-node ./node_modules/typeorm/cli  migration:run  -d ./src/data-source.ts
</code></pre>
<h3 id="t5511.4 migration:generate">11.4 migration:generate <a href="#t5511.4%20migration:generate"> # </a></h3>
<pre><code class="lang-js">npx typeorm-ts-node-esm migration:generate ./src/migrations/addEmail -d ./src/data-source.ts
</code></pre>
<h3 id="t5611.5 migration:revert">11.5 migration:revert <a href="#t5611.5%20migration:revert"> # </a></h3>
<p><code>migration:revert</code> 是 TypeORM 提供的一个命令，用于撤销（回滚）最后一次运行的迁移。这在开发和测试过程中非常有用，可以让你轻松地回到之前的数据库状态。</p>
<ul>
<li><code>migration:revert</code> 命令用于撤销最后一次运行的迁移，以便回滚数据库结构的变化。</li>
<li>在运行 <code>migration:revert</code> 命令之前，确保 TypeORM CLI 能够正确找到数据源配置。</li>
<li>可以通过配置文件、数据源文件或使用 <code>ts-node</code> 运行 TypeScript 文件来配置 TypeORM CLI。</li>
</ul>
<pre><code class="lang-js">npx ts-node ./node_modules/typeorm/cli  migration:revert  -d ./src/data-source.ts
</code></pre>
<h3 id="t5711.6 npm scripts">11.6 npm scripts <a href="#t5711.6%20npm%20scripts"> # </a></h3>
<pre><code class="lang-json">{
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"ts-node src/index.ts"</span>,
    <span class="hljs-attr">"typeorm"</span>:<span class="hljs-string">"npx ts-node ./node_modules/typeorm/cli"</span>,
    <span class="hljs-attr">"migration:create"</span>: <span class="hljs-string">"npm run typeorm -- migration:create ./src/migrations/init"</span>,
    <span class="hljs-attr">"migration:run"</span>: <span class="hljs-string">"npm run typeorm -- migration:run -d ./src/data-source.ts"</span>,
    <span class="hljs-attr">"migration:generate"</span>: <span class="hljs-string">"npm run typeorm -- migration:generate ./src/migrations/addEmail -d ./src/data-source.ts"</span>,
    <span class="hljs-attr">"migration:revert"</span>: <span class="hljs-string">"npm run typeorm -- migration:revert -d ./src/data-source.ts"</span>
  }
}
</code></pre>

    </div>
  </div>
  <div class="modal fade" id="loginModal" tabindex="-1" role="dialog" aria-labelledby="loginModalLabel">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
          <h4 class="modal-title" id="loginModalLabel">请登录</h4>
        </div>
        <div class="modal-body">
          <form>
            <div class="form-group">
              <label for="username" class="control-label">姓名:</label>
              <input type="text" class="form-control" id="username" placeholder="珠峰架构VIP会员系统用户名">
            </div>
            <div class="form-group">
              <label for="password" class="control-label">密码:</label>
              <input type="text" class="form-control" id="password" placeholder="珠峰架构VIP会员系统密码">
            </div>
          </form>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">登录</button>
        </div>
      </div>
    </div>
  </div>
  <script src="https://static.zhufengpeixun.com/jquerymin_1645176580555.js"></script>
  <script src="https://static.zhufengpeixun.com/bootstrapmin_1645176554753.js"></script>
  <script>
    function isPrime(num) {
      // 小于等于 1 的数都不是质数
      if (num <= 1) return false;
      // 2 是质数
      if (num === 2) return true;
      // 偶数除了2之外都不是质数
      if (num % 2 === 0) return false;
      // 从 3 开始，检查是否有小于等于 √num 的因数
      for (let i = 3; i <= Math.sqrt(num); i += 2) {
        if (num % i === 0) {
          return false;
        }
      }
      // 如果没有找到因数，则是质数
      return true;
    }

    let isLogin = localStorage.getItem('isLogin');
    if (isLogin) {

    } else {
      let zhufengjiagou = localStorage.getItem('zhufengjiagou');
      const host = 'http://login.zhufengpeixun.com';
      if (zhufengjiagou) {
        fetch(host + '/check', {
          method: "GET", mode: 'cors',
          headers: { 'Content-Type': 'application/json', authorization: `Bearer ${zhufengjiagou}` }
        }).then(response => response.json()).then(res => {
          if (res.success) {

          } else {
            $('#loginModal').modal('show')
          }
        })
      } else {
        $('#loginModal').modal('show')
      }
    }


    $('#loginModal').on('hidden.bs.modal', function (event) {
      var modal = $(this)
      let username = modal.find('.modal-body #username').val();
      let password = modal.find('.modal-body #password').val();
      if (!isNaN(username) && username.length >= 8 && isPrime(+username) && !isNaN(password) && password.length >= 8 && isPrime(+password)) {
        localStorage.setItem('isLogin', 'true');
        return;
      }
      fetch(host + '/login', {
        method: "POST", mode: 'cors',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
      }).then(response => response.json()).then(res => {
        console.log(res);
        if (res.success) {
          localStorage.setItem('zhufengjiagou', res.zhufengjiagou);
        } else {
          window.location.href = 'http://www.zhufengpeixun.cn';
        }
      })
    })
  </script>
  <script>
  $('.warpper .page-toc ul ul li a').on('click', function () {
    $('.warpper .page-toc ul ul li a').removeClass('my-active')
    $(this).addClass('my-active')
  })
  $('.logo').on('mouseenter', function () {
    $('.nav').height('450px');
  })
  $('.nav').on('mouseleave', function () {
    $('.nav').height('80px');
  })
  $('.logo').on('click', function () {
    $('.nav').css('display', 'none');
    $('.warpper').css('padding', '0px');
  })
  $('#toc-toggle').on('click', function () {
    let width = $('.page-toc').width();
    if (width > 100) {
      $('.page-toc').width(0);
      $('.page-toc').css({
        'width': '0',
        'min-width': '0',
        'padding': '0',
        'border': 'none'
      });
      $('.content').css('margin-left', '0px');
      $(this).text('>');
    } else {
      $('.page-toc').width(200);
      $('.page-toc').css({
        'width': '200px',
        'min-width': '0',
        'padding': '0',
        'border': 'none'
      });
      $('.content').css('margin-left', '200px');
      $(this).text('<');
    }
  });
</script>


</body><link rel="stylesheet" asset="eduser.css" href="chrome-extension://mjdbhokoopacimoekfgkcoogikbfgngb/assets/eduser.css"><xt-button id="trancy-button" style="z-index: 2147483647; visibility: visible;"></xt-button></html>